package com.lam.generator.common.util;

import java.io.File;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lam.common.config.AppBaseConfig;
import com.lam.common.exception.BusinessException;
import com.lam.common.utils.CollectionUtil;
import com.lam.common.utils.DateUtil;
import com.lam.common.utils.JsonUtil;
import com.lam.common.utils.StringUtils;
import com.lam.generator.common.constant.GeneratorConstant;
import com.lam.generator.entity.GenCodeModule;
import com.lam.generator.entity.GenTable;
import com.lam.generator.entity.GenTableColumn;

import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * 模板渲染工具类
 * 
 * @author lam
 */
public class RenderUtils {

	private static final Logger logger = LoggerFactory.getLogger(RenderUtils.class);

	/**
	 * 设置模板变量信息
	 *
	 * @return 模板列表
	 */
	public static Map<String, Object> prepareContext(GenTable genTable) {

		GenCodeModule codeModule = genTable.getCodeModule();
		String moduleName = codeModule.getModule();
		String businessName = genTable.getBusinessName();
		String functionName = genTable.getFunctionName();

		Map<String, Object> context = new HashMap<>();
		context.put("table", genTable);
		context.put("columns", genTable.getColumns());
		context.put("codeModule", codeModule);
		context.put("tableName", genTable.getTableName());
		context.put("functionName", StringUtils.isNotBlank(functionName) ? functionName
				: StringUtils.replace(genTable.getTableName(), "表", ""));
		context.put("ClassName", genTable.getClassName());
		context.put("className", StringUtils.uncapitalize(genTable.getClassName()));
		context.put("moduleName", StringUtils.uncapitalize(moduleName));
		context.put("ModuleName", StringUtils.capitalize(moduleName));
		context.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
		context.put("businessName", genTable.getBusinessName());
		context.put("packageName", codeModule.getPackageName());
		context.put("author", genTable.getFunctionAuthor());
		context.put("datetime", DateUtil.getCurDate());
		context.put("pkColumn", genTable.getPkColumn());
		
		context.put("permissionPrefix", String.format("%s:%s", codeModule.getModuleShort(), businessName));//权限前缀
		context.put("urlBase", String.format("/%s/%s", codeModule.getModuleShort(), businessName));//请求URL
		
		context.put("entityImportList", getEntityImportList(genTable));
		context.put("paramImportList", getParamImportList(genTable));
		context.put("entityImpImportList", getEntityImpImportList(genTable));
		context.put("hasSuperClass", genTable.getParentClass() != null);
		context.put("hasStrQueryField", hasStrQueryField(genTable.getColumns()));
		context.put("hasStrLikeQueryField", hasStrLikeQueryField(genTable.getColumns()));
		context.put("hasDictField", hasDictField(genTable.getColumns()));
		context.put("hasRequiredField", hasRequiredField(genTable.getColumns()));
		context.put("hasFuncList", genTable.hasFuncList());
		context.put("hasFuncGetById", genTable.hasFuncGetById());
		context.put("hasFuncUnique", genTable.hasFuncUnique());
		context.put("hasFuncInsert", genTable.hasFuncInsert());
		context.put("hasFuncUpdate", genTable.hasFuncUpdate());
		context.put("hasFuncDetail", genTable.hasFuncDetail());
		context.put("hasFuncDelete", genTable.hasFuncDelete());
		context.put("hasFuncExport", genTable.hasFuncExport());
		context.put("hasFuncImport", genTable.hasFuncImport());
		
		setOpContext(context, genTable);
		return context;
	}
	
	private static boolean hasStrLikeQueryField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->t.isQuery() && t.isStrDataType() && GeneratorConstant.QUERY_LIKE.equalsIgnoreCase(t.getQueryType()));
	}
	
	private static boolean hasStrQueryField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->t.isQuery() && t.isStrDataType());
	}
	
	private static boolean hasDictField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->StringUtils.isNotBlank(t.getDictCode()));
	}
	
	private static boolean hasRequiredField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->t.isRequired());
	}
	
	private static boolean hasRequiredStrField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->t.isRequired() && t.isStrDataType());
	}
	
	private static boolean hasRequiredNotStrField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->t.isRequired() && !t.isStrDataType());
	}
	
	private static boolean hasStringField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->t.isStrDataType());
	}

	private static void setOpContext(Map<String, Object> context, GenTable genTable) {
		Map<String, Object> paramsObj = JsonUtil.parseJSON(genTable.getOptions());
		//菜单信息
		String parentId = JsonUtil.getString(paramsObj, GeneratorConstant.PARENT_MENU_ID);
		context.put("parentMenuId", StringUtils.isBlank(parentId)? GeneratorConstant.DEFAULT_PARENT_MENU_ID : parentId);
		context.put("menuType", "1");//页面
//		context.put("pathModuleName", StringUtils.isBlank(genTable.getModuleName())?"":genTable.getModuleName());
		
		if(genTable.isSimpleTree()) {
			//tree结构信息
			context.put("treeCode", StringUtils.toCamelCase(JsonUtil.getString(paramsObj, GeneratorConstant.TREE_CODE)));
			context.put("treeParentCode", StringUtils.toCamelCase(JsonUtil.getString(paramsObj, GeneratorConstant.TREE_PARENT_CODE)));
			context.put("treeName", StringUtils.toCamelCase(JsonUtil.getString(paramsObj, GeneratorConstant.TREE_NAME)));
		}
		
		if((genTable.hasFuncInsert() || genTable.hasFuncUpdate() || genTable.hasFuncImport()) && genTable.hasFuncUnique()) {
			String businessName = genTable.getBusinessName();
			String uniqueColumn = JsonUtil.getString(paramsObj, GeneratorConstant.TABLE_UNIQUE_FIELD);
			String queryColumn = JsonUtil.getString(paramsObj, GeneratorConstant.TABLE_UNIQUE_QUERY_FIELD);
			String conditionColumn = JsonUtil.getString(paramsObj, GeneratorConstant.TABLE_UNIQUE_CONDITION_FIELD);
			GenTableColumn uniqueColumnObj = genTable.getColumns().stream().filter(t->t.getColumnName().equals(uniqueColumn)).collect(Collectors.toList()).get(0);
			uniqueColumnObj.setUnique(true);//设置唯一字段标志
			String uniqueField = uniqueColumnObj.getJavaField();
			String uniqueCode = uniqueField.contains(businessName) ? uniqueField: businessName + StringUtils.capitalize(uniqueField);
			
			StringBuilder sb = new StringBuilder();
			List<String> conditionFieldList = new ArrayList<>();
			if(StringUtils.isNotBlank(conditionColumn)) {
				String[] cs = conditionColumn.split(",");
				for (String c : cs) {
					conditionFieldList.add(StringUtils.toCamelCase(c));
					sb.append(" and ").append(c).append(" = {").append(StringUtils.toCamelCase(c)).append("}");
				}
			}
			
			//唯一编码信息
			context.put("uniqueCode", uniqueCode);
			context.put("uniqueCodeConstant", "UNIQUE_"+StringUtils.upperCase(StringUtils.camelToUnderline(uniqueCode)));
			context.put("uniqueColumn", uniqueColumn);
			context.put("uniqueField", uniqueField);
			GenTableColumn column = CollectionUtil.getFirst(genTable.getColumns().stream().filter(t->t.getJavaField().equals(uniqueField)).collect(Collectors.toList()));
			context.put("uniqueFieldRequired", column!=null?column.isRequired():false);
			context.put("uniqueFieldFirstU", StringUtils.capitalize(uniqueField));
			context.put("uniqueFieldName", uniqueColumnObj.getFormItemLabel());
			context.put("uniqueQueryColumn", queryColumn);
			context.put("uniqueQueryField", StringUtils.toCamelCase(queryColumn));
			context.put("hasConditionField", !conditionFieldList.isEmpty());
			context.put("conditionFieldList", conditionFieldList);
			context.put("conditionSql", sb.toString());
		}
	}
	
	private static boolean hasExcelIgnoreField(List<GenTableColumn> columns) {
		return columns.stream().anyMatch(t->!t.isList());
	}
	
	/**
	 * Entity类的导入包
	 * 
	 * @param genTable
	 * @return 返回需要导入的包列表
	 */
	private static Set<String> getEntityImportList(GenTable genTable) {
		Set<String> importList = new HashSet<>();
		if (genTable.getParentClass() != null) {
			importList.add(genTable.getParentClassName());
		}
		importList.add("com.baomidou.mybatisplus.annotation.TableName");
		importList.add("com.baomidou.mybatisplus.annotation.TableId");
		importList.add("com.baomidou.mybatisplus.annotation.IdType");
		importList.add("com.baomidou.mybatisplus.annotation.TableField");
		importList.add("com.baomidou.mybatisplus.annotation.FieldFill");
		importList.add("com.lam.common.base.domain.BaseEntity");
		importList.add("javax.validation.constraints.Null");
		importList.add("com.lam.common.validate.group.Add");
		
		if(genTable.hasFuncExport()) {
			importList.add("com.alibaba.excel.annotation.ExcelProperty");
			importList.add("com.alibaba.excel.annotation.write.style.ColumnWidth");
			if(hasExcelIgnoreField(genTable.getColumns())) {
				importList.add("com.alibaba.excel.annotation.ExcelIgnore");
			}
			if(hasDictField(genTable.getColumns())) {
				importList.add("com.lam.common.components.easyexcel.converter.DictConverter");
			}
		}
		
		if(hasRequiredStrField(genTable.getColumns())) {
			importList.add("javax.validation.constraints.NotBlank");
		}
		if(hasRequiredNotStrField(genTable.getColumns())) {
			importList.add("javax.validation.constraints.NotNull");
		}
		if(hasStringField(genTable.getColumns())) {
			importList.add("javax.validation.constraints.Size");
		}
		for (GenTableColumn column : genTable.getColumns()) {
			setImportByJavaType(importList, column, true);
			
			if (StringUtils.isNotBlank(column.getDictCode())) {
				importList.add("com.lam.common.components.dict.annotation.Dict");
				importList.add("com.lam.common.validate.annotation.DictCheck");
				importList.add("com.lam.common.validate.annotation.DictValid");
			}
		}
		return importList;
	}
	
	private static Set<String> getParamImportList(GenTable genTable){
		
		Set<String> importList = new HashSet<>();
		importList.add("com.lam.common.base.domain.BaseParam");
		for (GenTableColumn column : genTable.getColumns()) {
			if (column.isQuery()) {
				setImportByJavaType(importList, column, false);
				importList.remove("java.util.Date");
			}
		}
		return importList;
	}

	/**
	 * EntityImp类的导入包
	 * @param genTable
	 * @return 返回需要导入的包列表
	 */
	private static Set<String> getEntityImpImportList(GenTable genTable) {
		Set<String> importList = new HashSet<>();
		importList.add("com.alibaba.excel.annotation.ExcelProperty");
		importList.add("com.alibaba.excel.annotation.write.style.ColumnWidth");
		if(hasDictField(genTable.getColumns())) {
			importList.add("com.lam.common.components.dict.annotation.Dict");
			importList.add("com.lam.common.validate.annotation.DictValid");
		}
		for (GenTableColumn column : genTable.getColumns()) {
			if(column.isEdit()) {
				setImportByJavaType(importList, column, true);
			}
		}
		return importList;
	}

	private static void setImportByJavaType(Set<String> importList, GenTableColumn column, boolean useFormat) {

		if (GeneratorConstant.TYPE_DATE.equalsIgnoreCase(column.getJavaType())) {
			importList.add("java.util.Date");
			if(useFormat) {
				// 格式化注解需要的引入
				importList.add("com.fasterxml.jackson.annotation.JsonFormat");
				importList.add("com.lam.common.utils.DateUtil");
			}
		} else if (GeneratorConstant.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
			importList.add("java.math.BigDecimal");
		}
	}
	
	/**
	 * 获取文件名
	 */
	public static String getFilePath(String template, GenTable genTable) {
		GenCodeModule codeModule = genTable.getCodeModule();
		String packageName = codeModule.getPackageName();// 包路径
		String projectModule = codeModule.getProjectModule();//项目模块
		String className = genTable.getClassName();// 大写类名
		String businessName = genTable.getBusinessName();// 业务名称
		
		//后端Java代码生成地址
		String javaPath = File.separator + AppBaseConfig.me().getJavaProjectName() + File.separator + projectModule + GeneratorConstant.JAVA_MAIN_SRC + StringUtils.replace(packageName, ".", "/");
		String vuePath = File.separator + AppBaseConfig.me().getUiProjectName() + "/src";

		if (template.contains("entity.java")) {
			return MessageFormat.format("{0}/entity/{1}.java", javaPath, className);
		} 
		if (template.contains("param.java")) {
			return MessageFormat.format("{0}/param/{1}Param.java", javaPath, className);
		} 
		if (template.contains("entityImp.java")) {
			return MessageFormat.format("{0}/entity/imp/{1}Imp.java", javaPath, className);
		} 
		if (template.contains("uploadListener.java")) {
			return MessageFormat.format("{0}/listener/Upload{1}Listener.java", javaPath, className);
		} 
		if (template.contains("mapper.java")) {
			return MessageFormat.format("{0}/mapper/{1}Mapper.java", javaPath, className);
		} 
		if (template.contains("service.java")) {
			return MessageFormat.format("{0}/service/{1}Service.java", javaPath, className);
		} 
		if (template.contains("serviceImpl.java")) {
			return MessageFormat.format("{0}/service/impl/{1}ServiceImpl.java", javaPath, className);
		} 
		if (template.contains("controller.java")) {
			return MessageFormat.format("{0}/controller/{1}Controller.java", javaPath, className);
		} 
		if (template.contains("mapper.xml")) {
			return MessageFormat.format("{0}/mapper/xml/{1}Mapper.xml", javaPath, className);
		} 
		
		//vue文件
		if (template.contains("index.vue") || template.contains("index-tree.vue")) {
			return MessageFormat.format("{0}/views/{1}/{2}/index.vue", vuePath, codeModule.getUiPath(), businessName);
		} 
		if (template.contains("edit.vue")) {
			return MessageFormat.format("{0}/views/{1}/{2}/edit.vue", vuePath, codeModule.getUiPath(), businessName);
		} 
		
		//SQL文件
		if (template.contains("sql.sql")) {
			return businessName + ".sql";
		}
				
		//其他的Java片段单独放在other.java 中
		if (template.contains("other.java")) {
			return businessName + "_other.java";
		} 

		throw new BusinessException("不存在的模板:" + template);
	}
	
	/**
	 * 获取模板信息
	 *
	 * @return 模板列表
	 */
	private static List<String> prepareTemplateList(GenTable table) {
		List<String> templates = new ArrayList<>();
		templates.add("java/entity.java");
		templates.add("java/param.java");
		templates.add("java/mapper.java");
		templates.add("java/service.java");
		templates.add("java/serviceImpl.java");
		templates.add("xml/mapper.xml");
		if (StringUtils.isNotBlank(table.getFunctionList())) {
			templates.add("java/controller.java");
			if((table.hasFuncInsert() || table.hasFuncUpdate() || table.hasFuncImport()) && table.hasFuncUnique()) {
				templates.add("other/other.java");
			}
			if (table.hasFuncImport()) {
				templates.add("java/entityImp.java");
				templates.add("listener/uploadListener.java");
			}
			if (table.isSimpleTree()) {
				templates.add("vue/index-tree.vue");
			} else {
				templates.add("vue/index.vue");
			}
			if (table.hasFuncInsert() || table.hasFuncUpdate() || table.hasFuncDetail()) {
				templates.add("vue/edit.vue");
			}
			templates.add("sql/sql.sql");
		}
		Collections.sort(templates);
		return templates;
	}

	/**
	 * 生成代码
	 * 
	 * @param genTable
	 * @return <模板名称, 模板渲染后的内容>
	 */
	public static Map<String, String> generatorCode(GenTable genTable) {

		Map<String, String> dataMap = new LinkedHashMap<>();
		try {
			
			GenUtils.parseParentClass(genTable);
			// 查询列信息
			GenUtils.setPkColumn(genTable);
			// 获取模板列表
			List<String> templatePathList = prepareTemplateList(genTable);
			Map<String, Object> context = prepareContext(genTable);
			return renderContent(templatePathList, context);
		} catch (Exception e) {
			logger.error("渲染模板失败，表名：" + genTable.getTableName(), e);
		}
		return dataMap;
	}
	
	/**
	 * 渲染模板
	 * 
	 * @param template
	 * @param context
	 * @return
	 */
	private static Map<String, String> renderContent(List<String> templatePathList, Map<String, Object> context) {
		
		Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
        ClassTemplateLoader classTemplateLoader = new ClassTemplateLoader(RenderUtils.class, "/templates");
        cfg.setTemplateLoader(new MultiTemplateLoader(new TemplateLoader[]{ classTemplateLoader }));
        cfg.setEncoding(Locale.CHINA, "utf-8");
        cfg.setDefaultEncoding("utf-8");
        
        Map<String, String> dataMap = new LinkedHashMap<>();
        for (String templateName : templatePathList) {
			try {
				StringWriter sw = new StringWriter();
		        Template template = cfg.getTemplate(templateName, "utf-8");
		        template.process(context, sw);
				dataMap.put(templateName, sw.toString());
			} catch (Exception e) {
				logger.error("渲染模板异常：" + templateName, e);
			}
		}
        return dataMap;
	}
}
